package com.xyrl.project.manager.system.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xyrl.common.base.exception.XyrlException;
import com.xyrl.common.core.properties.XyrlProperties;
import com.xyrl.common.util.IpAddrUtil;
import com.xyrl.common.util.RedisUtil;
import com.xyrl.common.util.domain.XyrlResponse;
import com.xyrl.project.common.authentication.JWTToken;
import com.xyrl.project.common.authentication.JWTUtil;
import com.xyrl.project.common.authentication.LoginInfo;
import com.xyrl.project.common.base.service.CacheService;
import com.xyrl.project.common.common.service.AppService;
import com.xyrl.project.common.constant.XyrlConstant;
import com.xyrl.project.common.manager.UserManager;
import com.xyrl.project.common.system.dao.LoginLogMapper;
import com.xyrl.project.common.system.entity.ActiveUser;
import com.xyrl.project.common.system.entity.LoginLog;
import com.xyrl.project.common.system.entity.User;
import com.xyrl.project.common.system.entity.UserConfig;
import com.xyrl.project.common.system.service.LoginLogService;
import com.xyrl.project.common.system.service.UserService;
import com.xyrl.project.common.util.MD5Util;
import com.xyrl.project.common.util.XyrlUtil;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotBlank;
import java.util.*;

@Validated
@RestController
@Slf4j
public class LoginController {

    @Autowired
    private UserManager userManager;
    @Autowired
    private UserService userService;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private LoginLogService loginLogService;
    @Autowired
    private LoginLogMapper loginLogMapper;
    @Autowired
    private XyrlProperties properties;
    @Autowired
    private ObjectMapper mapper;
    @Autowired
    private AppService appService;


    @PostMapping("/login")
//    @Limit(key = "login", period = 60, count = 20, name = "登录接口", prefix = "limit")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username",value = "登录名",dataType = "String",paramType = "query"),
            @ApiImplicitParam(name = "password",value = "密码",dataType = "String",paramType = "query")}
    )
    public XyrlResponse login(
            @NotBlank(message = "{required}") String username,
            @NotBlank(message = "{required}") String password, HttpServletRequest request) throws Exception {
        username = StringUtils.lowerCase(username);
        password = MD5Util.encrypt(username, password);

        final String errorMessage = "用户名或密码错误";
        User user = this.userService.findByName(username);

        if (user == null || !XyrlConstant.USER_TYPE_SYSTEM.equalsIgnoreCase(user.getUserType())) {
            throw new XyrlException(errorMessage);
        }
        if (!StringUtils.equals(user.getPassword(), password)) {
            throw new XyrlException(errorMessage);
        }
        if (User.STATUS_LOCK.equals(user.getStatus())) {
            throw new XyrlException("账号已被锁定,请联系管理员！");
        }
        //设置用户最后登录时间
        user.setLastLoginTime(new Date());
        // 更新用户登录时间
        this.userService.updateLoginTime(username);
        //缓存用户登录信息
        this.cacheService.saveUser(user);
        // 保存登录记录
        LoginLog loginLog = new LoginLog();
        loginLog.setUsername(username);
        this.loginLogService.saveLoginLog(loginLog);
        LoginInfo info = new LoginInfo(username, XyrlConstant.USER_TYPE_SYSTEM);
        String token = XyrlUtil.encryptToken(JWTUtil.sign(info, password));
        log.info("取得用户token值:{}", token);
        long expireTime = System.currentTimeMillis() / 1000 + properties.getShiro().getJwtTimeOut();
        JWTToken jwtToken = new JWTToken(token, expireTime);
        String userId = this.saveTokenToRedis(info, jwtToken, request);
        user.setId(userId);

        Map<String, Object> userInfo = this.generateUserInfo(jwtToken, user);
        //添加ossUrl 附件地址
        userInfo.put("ossUrl", appService.getAppConfig().getImageDomain());
        return new XyrlResponse().message("认证成功").data(userInfo);
    }

    @GetMapping("index/{username}")
    public XyrlResponse index(@NotBlank(message = "{required}") @PathVariable String username) {
        Map<String, Object> data = new HashMap<>();
        // 获取系统访问记录
        Long totalVisitCount = loginLogMapper.findTotalVisitCount();
        data.put("totalVisitCount", totalVisitCount);
        Long todayVisitCount = loginLogMapper.findTodayVisitCount();
        data.put("todayVisitCount", todayVisitCount);
        Long todayIp = loginLogMapper.findTodayIp();
        data.put("todayIp", todayIp);
        // 获取近期系统访问记录
        List<Map<String, Object>> lastSevenVisitCount = loginLogMapper.findLastSevenDaysVisitCount(null);
        data.put("lastSevenVisitCount", lastSevenVisitCount);
        User param = new User();
        param.setAccount(username);
        List<Map<String, Object>> lastSevenUserVisitCount = loginLogMapper.findLastSevenDaysVisitCount(param);
        data.put("lastSevenUserVisitCount", lastSevenUserVisitCount);
        return new XyrlResponse().data(data);
    }

    @RequiresPermissions("user:online")
    @GetMapping("online")
    public XyrlResponse userOnline(String username) throws Exception {
        //取得在线用户
        Set<Object> userOnlineStringSet = RedisUtil.getSortSet(XyrlConstant.ACTIVE_USERS_ZSET_PREFIX, System.currentTimeMillis() / 1000, System.currentTimeMillis());
        //在线用户列表
        List<ActiveUser> activeUsers = new ArrayList<>();
        List<Object> list = RedisUtil.getMap(XyrlConstant.ACTIVE_USER_MAP_PREFIX, userOnlineStringSet);
        for (Object userOnlineString : list) {
            ActiveUser activeUser = mapper.readValue(String.valueOf(userOnlineString), ActiveUser.class);
            activeUser.setToken(null);
            if (StringUtils.isNotBlank(username)) {
                if (StringUtils.equalsIgnoreCase(username, activeUser.getUsername())) {
                    activeUsers.add(activeUser);
                }
            } else {
                activeUsers.add(activeUser);
            }
        }
        return new XyrlResponse().data(activeUsers);
    }

    @DeleteMapping("kickout/{id}")
    @RequiresPermissions("user:kickout")
    public void kickout(@NotBlank(message = "{required}") @PathVariable String id) throws Exception {
        // 取得登录token 值
        String token = (String) SecurityUtils.getSubject().getPrincipal();
        cacheService.clearCacheLoginUser(token);
    }

    @GetMapping("logout/{id}")
    public void logout(@NotBlank(message = "{required}") @PathVariable String id) throws Exception {
        this.kickout(id);
    }

    @PostMapping("regist")
    public void regist(
            @NotBlank(message = "{required}") String username,
            @NotBlank(message = "{required}") String password) throws Exception {
        this.userService.regist(username, password);
    }

    private String saveTokenToRedis(LoginInfo info, JWTToken token, HttpServletRequest request) throws Exception {
        String ip = IpAddrUtil.getIpAddr(request);
        //缓存用户登录信息 包括在线用户
        return cacheService.cacheLoginUser(ip, info, token.getToken());
    }

    /**
     * 生成前端需要的用户信息，包括：
     * 1. token
     * 2. Vue Router
     * 3. 用户角色
     * 4. 用户权限
     * 5. 前端系统个性化配置信息
     *
     * @param token token
     * @param user  用户信息
     * @return UserInfo
     */
    private Map<String, Object> generateUserInfo(JWTToken token, User user) {
        String username = user.getAccount();
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("token", token.getToken());

        Set<String> roles = this.userManager.getUserRoles(username);
        userInfo.put("roles", roles);

        Set<String> permissions = this.userManager.getUserPermissions(username);
        userInfo.put("permissions", permissions);

        UserConfig userConfig = this.userManager.getUserConfig(String.valueOf(user.getUserId()));
        userInfo.put("config", userConfig);

        user.setPassword("it's a secret");
        userInfo.put("user", user);
        return userInfo;
    }
}
